#!/usr/bin/env python

import wx
import app
import model
import dialog
import logging
import datetime
import component
import messaging.sms

class Main(component.Presenter):
    '''Main class
    
    @author Julot <andy.julot@gmail.com>
    @since Saturday, January 14, 2012. 09:07 AM'''
    
    LABEL_INBOX = 'Inbox'
    LABEL_ARCHIVE = 'Archive'
    LABEL_TRASH = 'Trash'
    
    def close(self):
        self.view.Close()
    
    _about = None
    
    def showAbout(self):
        if not self._about: self._about = dialog.About(self.view)
        self._about.Center()
        self._about.Show()

    def __init__(self, view, interactor):
        super(Main, self).__init__(view, interactor)
        self.readSashPos()
        self.setLabels()
        #self.readMessage()
        self._startTimer()

    def readSashPos(self):
        config = app.Config()
        key = '%s/SashV' % self.view.Name
        pos = config.ReadInt(key, -1)
        if pos != -1: self.view.vSplitter.SashPosition = pos
        key = '%s/SashH' % self.view.Name
        pos = config.ReadInt(key, -1)
        if pos != -1: self.view.hSplitter.SashPosition = pos

    def onClose(self):
        super(Main, self).onClose()
        self.writeSashPos()

    def writeSashPos(self):
        config = app.Config()
        key = '%s/SashV' % self.view.Name
        config.WriteInt(key, self.view.vSplitter.SashPosition)
        key = '%s/SashH' % self.view.Name
        config.WriteInt(key, self.view.hSplitter.SashPosition)
        
    def _startTimer(self):
        self._timer = wx.Timer(self.view)
        config = app.Config()
        interval = config.ReadInt('Modem/CheckInterval', 120)
        if interval: self._timer.Start(1000 * interval)

    def setLabels(self):
        '''Set navigation label'''
        
        labels = [self.LABEL_INBOX, self.LABEL_ARCHIVE, self.LABEL_TRASH]
        self.view.labels.values = labels

    def filter(self, value):
        self.view.busyInfo('Loading message...')
        query = model.Sms.objects.query().order_by('dateTime', 'desc')
        if value == self.LABEL_INBOX:
            query.filter(decompiled=1, archived=0, deleted=0, sequence__lt=2)
        elif value == self.LABEL_ARCHIVE:
            query.filter(decompiled=1, archived=1, deleted=0, sequence__lt=2)
        elif value == self.LABEL_TRASH:
            query.filter(decompiled=1, deleted=1, sequence__lt=2)
        self.view.messages.values = query
        self.view.idle()
    
    def loadModem(self):
        config = app.Config()
        port = config.Read('Modem/Port', 'COM1')
        baudrate = config.ReadInt('Modem/Baudrate', 9600)
        try:
            return component.Modem(port, baudrate)
        except:
            return None
    
    def onTimer(self):
        if not self.readMessage():
            s = 'Error connecting to modem.'
            self.view.status = s
    
    def readMessage(self):
        modem = self.loadModem()
        if not modem: return False
        
        modem.echoOff()
        modem.changeMode()
        modem.changeMessageStorage()
        pdus = modem.listMessage()
        # Because we are in GMT+7
        timeDelta = datetime.timedelta(hours=7)
        for index, pdu in pdus.iteritems():
            sms = model.Sms()
            sms.pdu = pdu
            sms.read = False
            sms.archived = False
            sms.deleted = False
            try:
                deliver = messaging.sms.SmsDeliver(pdu)
                sms.decompiled = True
            except:
                sms.decompiled = False
            if sms.decompiled:
                sms.dateTime = deliver.date + timeDelta
                sms.number = deliver.number.replace('+', '')
                sms.reference = 0
                sms.total = 1
                sms.sequence = 0
                if deliver.udh:
                    sms.reference = deliver.udh.concat.ref
                    sms.total = deliver.udh.concat.cnt
                    sms.sequence = deliver.udh.concat.seq
                sms.message = deliver.text
            sms.save()
            if not sms.decompiled:
                s = 'Undecompiled PDU saved to database id #%s'
                logging.warning(s % sms.id)
        
        for index in pdus:
            modem.deleteMessage(index)
        
        modem.close()
            
    def sendMessage(self, number, message):
        modem = self.loadModem()
        if not modem: return False
        
        sms = messaging.sms.SmsSubmit(number, message)
        r = True
        # Samsung Android phone must be set SMSC first.
        #modem.attention('+CSCA="+6281100000"')
        for pdu in sms.to_pdu():
            modem.attention('+CMGS=%d' % pdu.length)
            v = modem.attention('%s\x1a' % pdu.pdu, 1, False)
            if v.strip():
                s = 'SMS Send failed. Message from modem: %s' % v
                logging.warning(s)
                r = False
                break
        modem.close()
        
        return r
    
    _model = None
    
    def loadModel(self, id):
        if not self._model or self._model.id != id:
            self._model = model.Sms.objects.get(id)
        return self._model

    def viewMessage(self, id):
        sms = self.loadModel(id)
        
        sms.read = True
        sms.save()
        
        self.view.value.Value = sms.completeMessage
        
        menu = self.view.MenuBar
        tool = self.view.ToolBar
        menu.message.archive.Enable((not sms.archived) and (not sms.deleted))
        tool.archive.enable((not sms.archived) and (not sms.deleted))
        menu.message.delete.Enable(not sms.deleted)
        tool.delete.enable(not sms.deleted)
        menu.message.recover.Enable(sms.deleted)
        tool.recover.enable(sms.deleted)
    
    def clearMessage(self):
        menu = self.view.MenuBar
        tool = self.view.ToolBar
        menu.message.archive.Enable(False)
        tool.archive.disable()
        menu.message.delete.Enable(False)
        tool.delete.disable()
        menu.message.recover.Enable(False)
        tool.recover.disable()
        self.view.value.Value = ''
        
    def confirmExport(self):
        wildcard = 'Excel files (*.xls)|*.xls'
        s = datetime.datetime.now().strftime('%Y-%m-%d %H-%M')
        defaultFile = 'SMS %s.xls' % s
        style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
        return self.view.fileDialog(defaultFile=defaultFile, wildcard=wildcard,
                                    style=style)
    
    def export(self, path):
        self.view.busyInfo('Exporting data...')
        model.Sms.toExcel(path)
        self.view.idle()
    
    @property
    def selectedMessage(self):
        index = -1
        data = None
        if self.view.messages.GetSelectedItemCount():
            index = self.view.messages.GetFirstSelected()
            data = self.view.messages.GetItemData(index)
        return index, data
        
    def archive(self):
        index, data = self.selectedMessage
        self.view.messages.DeleteItem(index)
        sms = self.loadModel(data)
        sms.archived = True
        sms.save()
    
    def delete(self):
        index, data = self.selectedMessage
        self.view.messages.DeleteItem(index)
        sms = self.loadModel(data)
        sms.deleted = True
        sms.save()
    
    def recover(self):
        index, data = self.selectedMessage
        self.view.messages.DeleteItem(index)
        sms = self.loadModel(data)
        sms.deleted = False
        sms.save()
    
    def reply(self):
        dlg = dialog.Compose(self.view)
        index, data = self.selectedMessage
        if data:
            sms = self.loadModel(data)
            dlg.number.Value = sms.number
        if dlg.ShowModal() == wx.ID_OK:
            if not self.sendMessage(dlg.number.Value, dlg.message.Value):
                s = 'Message send failed.\r\nSee log for more detail.'
                self.view.messageDialog(s, 'Error')
        dlg.Destroy()
        